using System;
using System.Collections.Concurrent;

namespace Gj.MapData
{
    public class TaskBuilder
    {
        public static ConcurrentQueue<TaskBuilder> Queue = new();
        public static ConcurrentDictionary<string, Action> CallbackMap = new();
        private int _alloc;
        private DuplexData _content;

        private StandardExecutor _executor;
        private DefineData _from;
        private string _mapNoId;
        private byte _task;
        private string _uuid;

        public static TaskBuilder Get()
        {
            TaskBuilder builder = null;
            if (Queue.TryDequeue(out builder))
            {
            }

            if (builder == null) builder = new TaskBuilder();
            return builder;
        }

        public static TaskBuilder Get(StandardExecutor executor)
        {
            var builder = Get();
            builder.BindExecutor(executor);

            return builder;
        }

        internal static void Release(TaskBuilder builder)
        {
            if (builder._alloc == 0)
            {
                builder._executor = null;
                builder._from = null;
                builder._alloc = 0;
                builder._content = null;
                builder._uuid = "";
                Queue.Enqueue(builder);
            }
            else
            {
                builder._alloc--;
            }
        }

        public TaskBuilder Alloc()
        {
            _alloc++;
            return this;
        }

        public EventBuilder ToEvent()
        {
            var eventBuilder = EventBuilder.Get(_executor);

            eventBuilder.Task(this);
            return eventBuilder;
        }

        public TaskBuilder BindExecutor(StandardExecutor executor)
        {
            _executor = executor;
            _mapNoId = executor.CurrentMapNoId;
            return this;
        }

        public TaskBuilder BindMapNoId(string mapNoId)
        {
            _mapNoId = mapNoId;
            return this;
        }

        internal TaskContent Content()
        {
            var content = new TaskContent();
            content.From = _from;
            content.Task = _task;
            content.Content = _content;
            Release(this);
            return content;
        }

        public TaskBuilder From(DefineData from)
        {
            _from = from;
            return this;
        }

        public TaskBuilder One(byte task, DuplexData data)
        {
            _task = task;
            _content = data;
            return this;
        }

        public TaskBuilder Callback(Action callback)
        {
            var uuid = Guid.NewGuid().ToString("N");
            if (CallbackMap.TryAdd(uuid, callback))
            {
                _uuid = uuid;
            }
            else
            {
                uuid = Guid.NewGuid().ToString("N");
                if (CallbackMap.TryAdd(uuid, callback))
                    _uuid = uuid;
                else
                    throw new Exception("task Callback fail");
            }

            return this;
        }

        public void Task(long time = 0, object[] param = null)
        {
            _executor.Task(_mapNoId, _from?.Object(), _task, time, _content, param, _uuid);
            Release(this);
        }
    }
}